package org.tinygame.herostory.cmdHandler;

import com.google.protobuf.GeneratedMessageV3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinygame.herostory.GameMsgDecoder;
import org.tinygame.herostory.msg.GameMsgProtocol;
import org.tinygame.herostory.util.PackageUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 指令处理工厂
 * @author shkstart
 * @create 2022-05-05 15:26
 */
public final class CmdHandlerFactory {
    /**
     * 日志对象
     */
    private static final Logger LOGGER= LoggerFactory.getLogger(CmdHandlerFactory.class);
    /**
     * 处理器字典
     */
    private  static final Map<Class<?>,ICmdHandler<? extends GeneratedMessageV3>> _handlerMap=new HashMap<>();
    /**
     * 私有化类默认构造器
     */
    private CmdHandlerFactory(){

    }

    public static void init(){
        LOGGER.info("==== 开始 Cmd 和 Handler 的关联 ====");
        final String packageName = CmdHandlerFactory.class.getPackage().getName();
        //获取CmdHandlerFactory所在包下所有的类，包括子类
        Set<Class<?>> clazzSet=PackageUtil.listSubClazz(
                //
                packageName,
                true,
                ICmdHandler.class
        );
        //外层for循环，遍历所有类
        for(Class<?> clazz:clazzSet){
            //去掉抽象接口
            if((clazz.getModifiers()& Modifier.ABSTRACT)!=0){
                continue;
            }
            //获取该类的所有方法
            Method[] methodArray=clazz.getDeclaredMethods();

            Class<?> msgType=null; //定义消息类型
            //内层循环遍历该类中的所有方法，找到符合条件的handler方法
            for(Method currMethod:methodArray){
                //TODO 跳过桥接方法，桥接方法产生情景：在接口实现类中定义了非重写方法时，会出现桥接方法，名称为重写的方法即handle
                if(currMethod.isBridge()){
                    LOGGER.error("过滤了桥接方法:"+clazz.getName()+"."+currMethod.getName());
                    continue;
                }
                //跳过非handler方法
                if(!currMethod.getName().equals("handle")){
                    continue;
                }
                LOGGER.error(clazz.getName()+"  "+currMethod.getName());
                //获取该方法的所有参数
                Class<?>[] paramTypeArray= currMethod.getParameterTypes();
                //如果该参数不足两个，或者第二个参数不是GeneratedMessageV3类型，跳过该方法
                if(paramTypeArray.length<2 ||
                        //paramTypeArray[1] == GeneratedMessageV3.class||//TODO 这里也是为了过滤掉桥接方法，推荐使用isBridge那种方式过滤
                        !GeneratedMessageV3.class.isAssignableFrom(paramTypeArray[1])){
                    LOGGER.error(clazz.getName()+"  "+currMethod.getName()+"**"+paramTypeArray[0]+"**"+paramTypeArray[1]);
                    continue;
                }
                //方法和参数都正确的情况下，第二个参数就是消息类型，跳出方法遍历
                msgType=paramTypeArray[1];
                break;
            }
            //如果遍历该类中所有方法后，消息类型还是为null，
            // 则说明该类中没有符合条件的handler方法，
            // 遍历下一个类
            if(null==msgType){
                continue;
            }
            //到这里说明找到了该类的handler方法且消息类型正确幅值
            try {
                ICmdHandler<?> newHandler =(ICmdHandler<?>) clazz.newInstance();

                LOGGER.info("{} <==> {}",msgType.getName(),clazz.getName());

                _handlerMap.put(msgType,newHandler);
            } catch (Exception ex) {
                LOGGER.error(ex.getMessage(),ex);
            }
        }
    }


    /**
     * 从字典中获取消息对应的handler
     * @param msgClazz
     * @return
     */
    public static ICmdHandler<? extends GeneratedMessageV3> create(Class<?> msgClazz){
        if(null==msgClazz){
            return null;
        }
        return _handlerMap.get(msgClazz);
    }
}
